package com.jl.util;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @version 2012.10.27
 * @author LuHongjun
 */
public final class HexUtil {

	private HexUtil() {
	}

	private final static Pattern p = Pattern.compile("\\s+");

	private final static String SPACE = " ";

	/**
	 * get the string hexStr ,delete the char that is not the hex char or space.
	 * the result is like : XX XX X or XX XX,if the reslut is begin with 0X then
	 * delete 0.
	 * 
	 * @param hexStr
	 * @return
	 */
	public static String format(final String hexStr) {
		if (hexStr == null)
			return "";
		Matcher m = p.matcher(hexStr);
		String temp = m.replaceAll("");
		StringBuilder sb = new StringBuilder();
		int count = 0;
		for (char ch : temp.toCharArray()) {
			if (isHex(ch)) {
				sb.append(ch);
				count++;
				if (count % 2 == 0 && count != 0) {
					sb.append(' ');
				}
			}
		}
		return sb.toString().toUpperCase().trim();
	}

	/**
	 * delte the character that is not the hex number.
	 * 
	 * @param hexStr
	 * @return
	 */
	public static String compact(final String hexStr) {
		StringBuilder sb = new StringBuilder();
		for (char ch : hexStr.toCharArray()) {
			if (isHex(ch)) {
				sb.append(ch);
			}
		}
		return sb.toString().toUpperCase();
	}

	/**
	 * 
	 * @param ch
	 * @return
	 */
	public static boolean isHex(final char ch) {
		if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F')
				|| (ch >= 'a' && ch <= 'f')) {
			return true;
		}
		return false;
	}

	public static boolean strictCheck(final String hex) {
		char[] chs = hex.toCharArray();
		for (char ch : chs) {
			if (!isHex(ch)) {
				return false;
			}
		}
		return true;
	}

	public static boolean check(final String hex) {
		for (char ch : hex.toCharArray()) {
			if (!isHex(ch) && !Character.isWhitespace(ch)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * attention:this function will cast the int data to byte
	 * 
	 * @param data
	 * @return
	 */
	public static String toHexXX(final long data) {
		String hexStr = Long.toHexString(data & 0xFF);
		if (hexStr.length() % 2 == 1) {
			hexStr = "0" + hexStr;
		}
		return hexStr.toUpperCase().substring(hexStr.length() - 2);
	}

	public static String toHex(final long data, final int len) {
		String res = Long.toString(data, 16);
		while (res.length() < len * 2) {
			res = '0' + res;
		}
		return format(res);
	}

	public static String toHexString(final byte[] array) {
		if (array == null || array.length == 0) {
			return "";
		}
		StringBuilder sb = new StringBuilder();
		for (byte data : array) {
			sb.append(toHexXX(data)).append(SPACE);
		}
		return sb.toString();
	}
	
	public static String toHexCompact(final byte[] array) {
		if (array == null || array.length == 0) {
			return "";
		}
		StringBuilder sb = new StringBuilder();
		for (byte data : array) {
			sb.append(toHexXX(data));
		}
		return sb.toString();
	}
	
	public static String toHexCompact(final byte[] array,int off,int len) {
		if(off < 0 || off + len > array.length){
			throw new ArrayIndexOutOfBoundsException();
		}
		if (array == null || array.length == 0) {
			return "";
		}
		StringBuilder sb = new StringBuilder();
		for(int i = off,end = off + len; i < end; i++){
			sb.append(toHexXX(array[i]));
		}
		return sb.toString();
	}

	/**
	 * parse data to a byte
	 * 
	 * @param data
	 * @return
	 */
	public static byte parseHexToByte(String data) {
		data = HexUtil.compact(data);
		if (data.length() == 0) {
			throw new RuntimeException("the Hex string must not null or \"\"");
		}
		if (data.length() > 2) {
			data = data.substring(data.length() - 2);
		}
		return (byte) (Integer.parseInt(data, 16) & 0xFF);
	}

	private static byte parseHexHighSpeed(String data) {
		return (byte) (Integer.parseInt(data, 16) & 0xFF);
	}

	/**
	 * 
	 * @param data
	 * @return
	 */
	public static byte[] parseHexToByteArray(final String data) {
		if (!check(data)) {
			throw new NumberFormatException();
		}
		String dataTemp = format(data);
		if (dataTemp.equals("")) {
			return new byte[0];
		}
		String[] dataArray = p.split(dataTemp);
		byte[] array = new byte[dataArray.length];
		for (int i = 0, len = dataArray.length; i < len; i++) {
			array[i] = parseHexHighSpeed(dataArray[i]);
		}
		return array;
	}

	public static String toBianryString(final String hexStr) {
		byte[] hexArray = parseHexToByteArray(hexStr);
		return toBinaryString(hexArray);
	}

	/**
	 * tansform the byte data to a binary string like :00000001
	 * 
	 * @param data
	 * @return
	 */
	public static String toBinaryString(final byte data) {
		String res = Integer.toBinaryString(data & 0xFF);
		while (res.length() < 8) {
			res = "0" + res;
		}
		return res;
	}

	public static String toBinaryString(final byte[] datas) {
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < datas.length; i++) {
			byte data = datas[i];
			sb.append(toBinaryString(data));
			sb.append(SPACE);
		}
		return sb.toString();
	}

	/**
	 * transform the hex string to a decimal string
	 * 
	 * @param hex
	 * @return
	 */
	public static String toDecimalString(final String hex) {
		String temp = HexUtil.compact(hex);
		BigInteger bigInt = new BigInteger(temp, 16);
		return bigInt.toString(10);
	}

	/**
	 * 
	 * @param hex
	 * @param array
	 * @return
	 */
	public static String getHexBit(final String hex, final int[] array) {
		String binary = toBianryString(hex).trim();
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < array.length; i++) {
			sb.append(binary.charAt(8 - array[i]));
		}
		return sb.toString();
	}

	/**
	 * return the XX count of the hex string
	 */
	public static int getHexCount(String hex) {
		hex = HexUtil.compact(hex);
		int count = hex.length() / 2;
		return count;
	}

	/**
	 * 
	 * @param begin
	 *            the begin from 0.
	 * @param end
	 *            is not contain the end index
	 * @return
	 */
	public static String subHex(final String hex, int begin, int end) {
		begin = begin & 0xFFFFFFFF;
		end = end & 0xFFFFFFFF;
		byte[] data = HexUtil.parseHexToByteArray(hex);
		if (end > data.length) {
			throw new IndexOutOfBoundsException();
		}
		data = Arrays.copyOfRange(data, begin, end);
		return HexUtil.toHexString(data);
	}

	public static int makeInt(byte[] array, int off, int len) {
		int value = 0;
		if (len > 4 || len <= 0) {
			throw new RuntimeException(
					"length must be less than 4 and biger than 0");
		}
		for (int i = 0; i < len; i++) {
			value |= array[off + i] & 0xFF;
			if (i != len - 1) {
				value <<= 8;
			}
		}
		return value & 0xFFFFFFFF;
	}

	public static short makeShort(final byte[] array, final int off) {
		short value = (short) (array[off] & 0xFF);
		value <<= 8;
		value |= array[off + 1] & 0xFF;
		return value;
	}

	public static byte highByte(final short value) {
		return (byte) ((value >> 8) & 0xFF);
	}

	public static byte lowByte(short value) {
		return (byte) (value & 0xFF);
	}

	public static String toHexString(final byte[] array, final int off,
			final int len) {
		if (array == null || array.length == 0) {
			return "";
		}
		StringBuilder sb = new StringBuilder();
		int end = off + len;
		for (int i = off; i < end; i++) {
			byte data = array[i];
			sb.append(toHexXX(data)).append(SPACE);
		}
		return sb.toString();
	}

	/**
	 * Hex-dump a byte array (offset and printable ASCII included)
	 * <p>
	 * 
	 * @param data
	 *            Byte array to convert to HexString
	 * @param offset
	 *            Start dump here
	 * @param len
	 *            Number of bytes to be dumped.
	 * @return HexString
	 */
	public static String dump(final byte[] data, final int offset, int len) {
		if (data == null)
			return "null";

		char[] ascii = new char[16];

		StringBuffer out = new StringBuffer(256);

		if (offset + len > data.length) {
			len = data.length - offset;
		}

		for (int i = offset; i < offset + len;) {
			// offset
			out.append(toHexXX((i >>> 8) & 0xff));
			out.append(toHexXX(i & 0xff));
			out.append(":  ");

			// hexbytes
			for (int j = 0; j < 16; j++, i++) {
				if (i < (offset + len)) {
					int b = data[i] & 0xff;
					out.append(toHexXX(b)).append(' ');
					ascii[j] = (b >= 32 && b < 127) ? (char) b : '.';
				} else {
					out.append("   ");
					ascii[j] = ' ';
				}
			}

			// ASCII
			out.append(' ').append(ascii).append("\n");
		}
		return out.toString().trim();
	}

	public static String dump(final byte[] array) {
		if (array == null) {
			return "null";
		}
		return dump(array, 0, array.length);
	}

	/**
	 * contac two byte array
	 * 
	 * @param b1
	 * @param b2
	 * @return
	 */
	public static byte[] contacByteArray(final byte[] b1, final byte[] b2) {
		byte[] res = new byte[b1.length + b2.length];
		System.arraycopy(b1, 0, res, 0, b1.length);
		System.arraycopy(b2, 0, res, b1.length, b2.length);
		return res;
	}

	public static boolean arrayEqusls(final byte[] b1, final byte[] b2) {
		int len1 = b1.length;
		int len2 = b2.length;
		if (len1 != len2) {
			return false;
		}
		for (int i = 0; i < len1; i++) {
			if (b1[i] != b2[i]) {
				return false;
			}
		}
		return true;
	}

	public static boolean compare(String exp, String real) {
		real = HexUtil.compact(real);
		exp = exp.replaceAll("\\s+", "").toUpperCase();
		if (exp.length() != real.length()) {
			return false;
		}
		for (int i = 0; i < exp.length(); i++) {
			char ch = exp.charAt(i);
			if (ch != 'X') {
				if (ch != real.charAt(i)) {
					return false;
				}
			}
		}
		return true;
	}
	
	public static byte[] unwrap(Byte[] _byte){
		byte[] res = new byte[_byte.length];
		int index = 0;
		for(Byte b : _byte){
			res[index++] = b;
		}
		return res;
	}
	
	public static Byte[] wrap(byte[] _byte){
		Byte[] res = new Byte[_byte.length];
		int index = 0;
		for(byte b : _byte){
			res[index++] = b;
		}
		return res;
	}
}
